package com.nulldev.util.web.nTube.pages;

import static com.nulldev.util.web.nTube.pages.Urlizer.urlizer;

import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

import com.nulldev.util.JVM.Arguments;
import com.nulldev.util.concurrency.threadIt.v4.emapi.ExecutorManager;
import com.nulldev.util.data.Arrays.arrays.FastLists;
import com.nulldev.util.data.Arrays.maps.FastMaps;
import com.nulldev.util.internal.backport.arrays.BackportList;
import com.nulldev.util.io.file.FileUtil;
import com.nulldev.util.logging.Logger;
import com.nulldev.util.logging.LoggerFactory;
import com.nulldev.util.manage.experiments.Experiments;
import com.nulldev.util.web.HttpServer3.Response;
import com.nulldev.util.web.HttpServer3.pages.v3.HttpPage3;
import com.nulldev.util.web.URLUtils.URLUtils;
import com.nulldev.util.web.YouTubeExtractor.YouTubeExtractor;
import com.nulldev.util.web.YouTubeExtractor.YouTubeExtractor.ExtractorType;
import com.nulldev.util.web.YouTubeExtractor.extractors.IYouTubeExtractorConstants;
import com.nulldev.util.web.YouTubeExtractor.formats.AudioCodec;
import com.nulldev.util.web.YouTubeExtractor.formats.Format;
import com.nulldev.util.web.YouTubeExtractor.formats.VideoCodec;
import com.nulldev.util.web.YouTubeExtractor.video.ExtractedVideo;
import com.nulldev.util.web.YouTubeExtractor.video.YouTubeVideo;
import com.nulldev.util.web.legacyURI.LegacyURIUtils;
import com.nulldev.util.web.nTube.NTubeLaunchArguments.nTubeArguments;
import com.nulldev.util.web.nTube.NTubeServer;

public class FlashWatchPage extends HttpPage3 {

	private final static Logger log = LoggerFactory.getLogger();
	private static final boolean ENABLE_SECURITY_HARDENING = Experiments.get("ntube://enable-security-hardening/").isActive();

	public FlashWatchPage() {
		super("/watch_flash");
		if (ENABLE_SECURITY_HARDENING) {
			super.setChecks(EnhancedSecurityChecks.DISALLOW_POST_DATA, EnhancedSecurityChecks.ONLY_ALLOW_BASIC_METHODS);
		}
	}

	@SuppressWarnings("resource")
	@Override
	public Response onRequest(final Request request) {
		final Map<String, String> s = URLUtils.parseQuery(URLUtils.parseURL_NoAccel(request.url()));
		if (!s.containsKey("v"))
			return new Response(404);
		if (s.isEmpty() && !s.containsKey("v") && !request.getHeaders().containsKey("Referrer") && !request.getHeaders().get("Referrer").contains("?v=")) {
			return new Response(404, createStream("<html><body>No video ID provided. (use ?v=*videoID* at the end of the URL)</body></html>"));
		}
		final String v = (s.containsKey("v") ? s.get("v") : URLUtils.parseQuery(URLUtils.parseURL(request.getHeaders().get("Referrer"))).get("v"));
		if (v == null || v.isEmpty()) {
			return new Response(400);
		}
		final Map<String, Object> map = FastMaps.fixedUniMap(new Object[][]
			{
					{ IYouTubeExtractorConstants.MAP_TYPE_VIDEO_ID, v } });
		final ExtractedVideo result = ExecutorManager.get().asyncSubmit(YouTubeExtractor.get(ExtractorType.PAGE).generateTask(map)).waitTillCompletion();
		if (result == null || result.getResult() == null || !result.getResult().successful()) {
			String out = FileUtil.getResourceAsString(NTubeServer.class, "video_noscript.html");
			out = out.replaceAll(Pattern.quote("$Fallback.VideoTitle"), "Failed to extract video.");
			log.error("Failed to extract fallback video!");
			log.error("Result: " + result);
			return new Response(200, createStream(out));
		}
		if (result.supportsLateExtraction())
			result.awaitCompletion();
		final List<YouTubeVideo> videos = result.videos();
		final List<YouTubeVideo> combinedStreams = FastLists.list(3);
		for (final YouTubeVideo e : videos) {
			final Format f = e.getFormat();
			if (!f.getAudioCodec().equals(AudioCodec.NONE) && !f.getVideoCodec().equals(VideoCodec.NONE)) {
				if (s.containsKey("openCodecsOnly") && !f.getVideoCodec().isOpenCodec()) {
					continue;
				} else {
					combinedStreams.add(e);
				}
			}
		}
		BackportList.sort(combinedStreams, new Comparator<YouTubeVideo>() {

			@Override
			public int compare(final YouTubeVideo o1, final YouTubeVideo o2) {
				return Integer.valueOf(o1.getFormat().getHeight()).compareTo(o2.getFormat().getHeight());
			}
		});
		if (combinedStreams.isEmpty()) {
			String out = FileUtil.getResourceAsString(NTubeServer.class, "video_noscript.html");
			out = out.replaceAll(Pattern.quote("$Fallback.VideoTitle"), "Failed to extract video.");
			log.error("Failed to extract fallback video!");
			log.error("Result: " + result);
			return new Response(200, createStream(out));
		}
		String out = FileUtil.getResourceAsString(NTubeServer.class, "video_flash.html");
		out = out.replaceAll(Pattern.quote("$Fallback.src"), LegacyURIUtils.safeEncode(urlizer(combinedStreams.get(combinedStreams.size() - 1).videoURL())));
		out = out.replaceAll(Pattern.quote("$Fallback.VideoTitle"), result.getMetadata().getTitle());
		if (Arguments.notAnArgument(nTubeArguments._disableGC)) {
			videos.clear();
			result.clear();
			combinedStreams.clear();
			log.trace("Created GC layer.");
		}
		return new Response(200, createStream(out)).setCustomMime("text/html");
	}

}
